package com.mxpio.erp.inventory.policy.impl;

import com.mxpio.erp.common.LotStatusUtil;
import com.mxpio.erp.common.enums.OrderEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.inventory.entity.*;
import com.mxpio.erp.common.jpa.model.LotKey;
import com.mxpio.erp.common.jpa.model.Order;
import com.mxpio.erp.common.order.policy.OrderContext;
import com.mxpio.erp.common.order.service.OrderLineService;
import com.mxpio.erp.common.quality.entity.SoInQualityOrder;
import com.mxpio.erp.common.quality.entity.SoInQualityOrderLine;
import com.mxpio.erp.common.sales.entity.*;
import com.mxpio.erp.common.vo.NoticeClassVo;
import com.mxpio.erp.common.vo.OrderExcuteLineVo;
import com.mxpio.erp.inventory.policy.AbstractWoPolicy;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.jpa.query.Operator;
import com.mxpioframework.security.util.SecurityUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * @Author : 吴盼盼
 * @Datetime : 2022/12/1 22:24
 * @Desc :
 * @Modor :  Modifytime:
 * @modDesc :销售订单对应仓单执行
 */
public class SaleWoPolicy extends AbstractWoPolicy {
    private List<OrderExcuteLineVo> orderExcuteVos;
    private OrderLineService orderLineService;
    public SaleWoPolicy(List<OrderExcuteLineVo> orderExcuteVos,OrderLineService orderLineService) {
        this.orderExcuteVos = orderExcuteVos;
        this.orderLineService = orderLineService;
    }

    public void execute(OrderContext context) {
        Order order = context.getEntity();
        if(order instanceof WarehouseOrder) {
            List<OrderExcuteLineVo> orderExcuteVos = context.getOrderExcuteVos();
            List<InvFlow> invFlows = new ArrayList<>();

            Map<String, BigDecimal> sumQuantity = new HashMap<String, BigDecimal>();
            List<OrderExcuteLineVo> orderExcuteVosNoLotNo = new ArrayList<>();//无批次的出库明细
            String whCode = ((WarehouseOrder) order).getWhCode();//仓库编码
            String accessType = ((WarehouseOrder) order).getAccessType();
            for(OrderExcuteLineVo vo : orderExcuteVos) {
                InvFlow invFlow = new InvFlow();
                invFlow.setAccessType(accessType);
                invFlow.setItemCode(vo.getItemCode());
                invFlow.setWhCode(((WarehouseOrder) order).getWhCode());
                // 批次控制
                if(OrderEnums.AccessType.IN.getCode().equals(accessType)) {
                    if(vo.getLotNo() == null) {
                        Lot newLot = context.getLotService().getNewLot(order, vo);
                        invFlow.setLotNo(newLot.getLotNo());
                        invFlow.setSubLotIndex(newLot.getSubLotIndex());
                    }else {
                        invFlow.setLotNo(vo.getLotNo());
                        String subLotIndex = StringUtils.isBlank(vo.getSubLotIndex())?"0":vo.getSubLotIndex();
                        invFlow.setSubLotIndex(subLotIndex);
                        LotKey lotKey = new LotKey();
                        lotKey.setLotNo(vo.getLotNo());
                        lotKey.setSubLotIndex(subLotIndex);
                        Lot dbLot = JpaUtil.getOne(Lot.class, lotKey);
                        if (dbLot==null){//保存批次
                            Lot lot = new Lot();
                            lot.setLotNo(vo.getLotNo());
                            lot.setLotStatus(LotStatusUtil.toBinaryString("0"));
                            lot.setSubLotIndex(subLotIndex);
                            JpaUtil.save(lot);
                        }
                    }
                }else {
                    if(vo.getLotNo() == null) {
						/*context.addError("未分配批次");
						break;*/
                        orderExcuteVosNoLotNo.add(vo);
                        continue;
                    }else {
                        invFlow.setLotNo(vo.getLotNo());
                        invFlow.setSubLotIndex(vo.getSubLotIndex());
                    }
                }
                invFlow.setQuantity(vo.getQuantity());
                // 库存数量控制
                if(OrderEnums.AccessType.OUT.getCode().equals(accessType)) {
                    BigDecimal stock = context.getLotService().getStock(invFlow.getWhCode(), invFlow.getItemCode(), invFlow.getLotNo(),invFlow.getSubLotIndex(),false);
                    if(stock.compareTo(invFlow.getQuantity()) < 0) {
                        context.addError("物料：" + invFlow.getItemCode() + "库存不足");
                        break;
                    }
                }

                // 单据数量控制
                BigDecimal completeQuantity = context.getLotService().getMaxQuantityByWo(vo.getBizWoNo(), vo.getBizWoLineNo());
                BigDecimal currentQuan = sumQuantity.get(vo.getBizWoNo() + "#" + vo.getBizWoLineNo());
                if(currentQuan == null) {
                    BigDecimal maxQuantity = vo.getLineQuantity()==null?BigDecimal.ZERO:vo.getLineQuantity();
                    currentQuan = maxQuantity.subtract(completeQuantity).subtract(vo.getQuantity());
                    sumQuantity.put(vo.getBizWoNo() + "#" + vo.getBizWoLineNo(),currentQuan);//处理拆批情况
                }else {
                    currentQuan = currentQuan.subtract(vo.getQuantity());
                }
                if(currentQuan.compareTo(BigDecimal.ZERO) < 0) {
                    context.addError("物料：" + invFlow.getItemCode() + "超过最大执行数量");
                    break;
                }

                invFlow.setMemo(((WarehouseOrder) order).getMemo());
                invFlow.setOriginBizNo(((WarehouseOrder) order).getOriginBizNo());
                invFlow.setOriginBizType(((WarehouseOrder) order).getOriginBizType());
                invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
                invFlow.setWoBizNo(((WarehouseOrder) order).getBizNo());
                invFlow.setWoBizType(((WarehouseOrder) order).getWoType());
                invFlow.setWoBizLineNo(vo.getBizWoLineNo());
                invFlows.add(invFlow);
            }
            //处理出库无批次明细数据
            if (orderExcuteVosNoLotNo.size()>0){
                Map<String, BigDecimal> sumUseItemQuantityMap = new HashMap<String, BigDecimal>();//汇总明细行中相同物料情况，

                Map<String,List<VInvLotStock>> stockListMap = new HashMap<>();
                List<InvFlow> insertFlowList = new ArrayList<>();
                Map<String,BigDecimal> usedStockMap = new HashMap<>();//批次已占用库存
                for(OrderExcuteLineVo vo : orderExcuteVosNoLotNo) {
                    // 库存数量控制
                    if(OrderEnums.AccessType.OUT.getCode().equals(accessType)) {
                        BigDecimal stock = context.getLotService().getItemLiveStockWithLotStatus(whCode, vo.getItemCode(),true);
                        BigDecimal useQuantitySum = sumUseItemQuantityMap.get(vo.getItemCode());//该循环中，该物料对应行已经占用数量和
                        if (useQuantitySum==null){
                            sumUseItemQuantityMap.put(vo.getItemCode(),vo.getQuantity());
                            if(stock.compareTo(vo.getQuantity()) < 0) {
                                context.addError("物料：" + vo.getItemCode() + "实时库存不足");
                                break;
                            }
                        }else{
                            if(stock.compareTo(vo.getQuantity().add(useQuantitySum)) < 0) {
                                context.addError("物料：" + vo.getItemCode() + "实时库存不足");
                                break;
                            }
                            sumUseItemQuantityMap.put(vo.getItemCode(),vo.getQuantity().add(useQuantitySum));//累计数量
                        }
                    }
                    // 单据数量控制
                    BigDecimal completeQuantity = context.getLotService().getMaxQuantityByWo(vo.getBizWoNo(), vo.getBizWoLineNo());
                    BigDecimal currentQuan = sumQuantity.get(vo.getBizWoNo() + "#" + vo.getBizWoLineNo());//剩余最大可执行数量
                    if(currentQuan == null) {
                        BigDecimal maxQuantity = vo.getLineQuantity()==null?BigDecimal.ZERO:vo.getLineQuantity();
                        currentQuan = maxQuantity.subtract(completeQuantity).subtract(vo.getQuantity());
                        sumQuantity.put(vo.getBizWoNo() + "#" + vo.getBizWoLineNo(),currentQuan);//处理拆批情况
                    }else {
                        currentQuan = currentQuan.subtract(vo.getQuantity());
                    }
                    if(currentQuan.compareTo(BigDecimal.ZERO) < 0) {
                        context.addError("物料：" + vo.getItemCode() + "超过最大执行数量");
                        break;
                    }

                    //开始匹配库存
                    List<VInvLotStock> itemLotStockList = null;
                    if (stockListMap.containsKey(vo.getItemCode())){
                        itemLotStockList = stockListMap.get(vo.getItemCode());
                    }else{
                        //根据先进先出，查询批次
                        Criteria lotStockCriteria = Criteria.create().addCriterion("whCode", Operator.EQ, whCode).
                                addCriterion("itemCode",Operator.EQ, vo.getItemCode()).
                                addCriterion("lotStatus",Operator.EQ, LotStatusUtil.toBinaryString("0"));//增加批次状态过滤

                        lotStockCriteria.addOrder(new com.mxpioframework.jpa.query.Order("createTime",false));//根据创建时间，先进(批次号小)先出。
                        itemLotStockList = JpaUtil.linq(VInvLotStock.class).gt("quantity",0).where(lotStockCriteria).list();//过滤调批次为零的
                        stockListMap.put(vo.getItemCode(),itemLotStockList);
                    }
                    BigDecimal quantity = vo.getQuantity();//vo行待执行数据
                    for (VInvLotStock stock:itemLotStockList){
                        BigDecimal stockQuantity = stock.getQuantity();//该批次库存数量
                        if (usedStockMap.containsKey(stock.getLotNo())){
                            BigDecimal used = usedStockMap.get(stock.getLotNo());
                            stockQuantity = stockQuantity.subtract(used);
                        }
                        if (stockQuantity.compareTo(BigDecimal.ZERO)<=0){//库存小于等于0，直接跳过
                            continue;
                        }
                        if (quantity.compareTo(stockQuantity)>0){//大于该批次库存数量
                            InvFlow invFlow = new InvFlow();
                            invFlow.setAccessType(accessType);
                            invFlow.setItemCode(vo.getItemCode());
                            invFlow.setWhCode(whCode);
                            invFlow.setQuantity(stockQuantity);//取库存批次数量
                            invFlow.setMemo(((WarehouseOrder) order).getMemo());
                            invFlow.setOriginBizNo(((WarehouseOrder) order).getOriginBizNo());
                            invFlow.setOriginBizType(((WarehouseOrder) order).getOriginBizType());
                            invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
                            invFlow.setWoBizNo(((WarehouseOrder) order).getBizNo());
                            invFlow.setWoBizType(((WarehouseOrder) order).getWoType());
                            invFlow.setWoBizLineNo(vo.getBizWoLineNo());
                            invFlow.setLotNo(stock.getLotNo());
                            invFlow.setSubLotIndex(stock.getSubLotIndex());//增加子批索引
                            quantity = quantity.subtract(stockQuantity);//
                            insertFlowList.add(invFlow);
                            if (usedStockMap.containsKey(invFlow.getLotNo())){
                                BigDecimal used = usedStockMap.get(invFlow.getLotNo());
                                usedStockMap.put(invFlow.getLotNo(),used.add(invFlow.getQuantity()));
                            }else{
                                usedStockMap.put(invFlow.getLotNo(),invFlow.getQuantity());
                            }
                        }else{//小于等于批次库存的情况
                            InvFlow invFlow = new InvFlow();
                            invFlow.setAccessType(accessType);
                            invFlow.setItemCode(vo.getItemCode());
                            invFlow.setWhCode(whCode);
                            invFlow.setQuantity(quantity);
                            invFlow.setMemo(((WarehouseOrder) order).getMemo());
                            invFlow.setOriginBizNo(((WarehouseOrder) order).getOriginBizNo());
                            invFlow.setOriginBizType(((WarehouseOrder) order).getOriginBizType());
                            invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
                            invFlow.setWoBizNo(((WarehouseOrder) order).getBizNo());
                            invFlow.setWoBizType(((WarehouseOrder) order).getWoType());
                            invFlow.setWoBizLineNo(vo.getBizWoLineNo());
                            invFlow.setLotNo(stock.getLotNo());
                            invFlow.setSubLotIndex(stock.getSubLotIndex());//增加子批索引
                            //JpaUtil.save(invFlow);
                            insertFlowList.add(invFlow);
                            if (usedStockMap.containsKey(invFlow.getLotNo())){
                                BigDecimal used = usedStockMap.get(invFlow.getLotNo());
                                usedStockMap.put(invFlow.getLotNo(),used.add(invFlow.getQuantity()));
                            }else{
                                usedStockMap.put(invFlow.getLotNo(),invFlow.getQuantity());
                            }
                            break;//跳出
                        }
                    }
                }
                if (insertFlowList.size()>0){
                    invFlows.addAll(insertFlowList);//添加到待存列表中
                }
            }

            if(CollectionUtils.isEmpty(context.getErrors())) {
                JpaUtil.save(invFlows);
            }
        }else {
            context.addError("单据类型错误");
        }
    }

    @Override
    public OrderContext config(OrderContext context) {
        context.setOrderExcuteVos(orderExcuteVos);
        return context;
    }

    @Override
    public boolean beforeExecute(OrderContext context) {
        return true;
    }

    @Override
    public void afterExecute(OrderContext context) {
        if(CollectionUtils.isEmpty(context.getErrors())){
            Order wo = context.getEntity();
            if (judgeWOCompletion(context)){
                if(StringUtils.isBlank(wo.getExecutor())) {
                    wo.setExecuteTime(new Date());
                    wo.setExecutor(SecurityUtils.getLoginUsername());
                }
                wo.setOrderStatus(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode());
                JpaUtil.update(wo);
            }else{
                if(OrderStatusEnums.OrderStatus.ORDERED.getStatusCode().equals(wo.getOrderStatus())||
                        OrderStatusEnums.OrderStatus.PLAN.getStatusCode().equals(wo.getOrderStatus())) {
                    wo.setOrderStatus(OrderStatusEnums.OrderStatus.PROCESSING.getStatusCode());
                    wo.setExecuteTime(new Date());
                    wo.setExecutor(SecurityUtils.getLoginUsername());
                    JpaUtil.update(wo);
                }
            }
            //判断销售订单执行，如果完成更新状态
            if (wo instanceof WarehouseOrder){
                WarehouseOrder warehouseOrder = (WarehouseOrder) wo;
                String originBizNo = warehouseOrder.getOriginBizNo();//源单编码
                //考虑手工新增的仓单，源单编码为空的情况
                if(StringUtils.isNotBlank(originBizNo)){
                    SalesOrder salesOrder = JpaUtil.getOne(SalesOrder.class, originBizNo);
                    boolean salesOrderCompletionFlag = true;//销售订单是否完成
                    if (salesOrder!=null){
                        List<SalesOrderLine> salesOrderLines = JpaUtil.linq(SalesOrderLine.class).equal("bizNo", salesOrder.getBizNo()).list();
                        if (salesOrderLines.size()>0){
                            NoticeClassVo shippingNoticeClazz = new NoticeClassVo(ShippingNotice.class, ShippingNoticeLine.class);
                            NoticeClassVo rejectBillNoticeClazz = new NoticeClassVo(RejectBill.class, RejectBillLine.class);
                            NoticeClassVo inQualityOrderClazz = new NoticeClassVo(SoInQualityOrder.class, SoInQualityOrderLine.class);//增加入库验收单 数据统计
                            List<NoticeClassVo> noticeClassVos = new ArrayList<NoticeClassVo>();
                            noticeClassVos.add(shippingNoticeClazz);
                            noticeClassVos.add(rejectBillNoticeClazz);
                            noticeClassVos.add(inQualityOrderClazz);
                            orderLineService.handleBizOrderLineQuantity(salesOrderLines, noticeClassVos, OrderEnums.AccessType.OUT.getCode());

                            for (SalesOrderLine salesOrderLine:salesOrderLines){
                                BigDecimal quantity = salesOrderLine.getQuantity();//订单行数量
                                BigDecimal actualQuantity = salesOrderLine.getActualQuantity();//已执行数量
                                BigDecimal actualRejectQuantity = salesOrderLine.getActualRejectQuantity();//已执行退货数量
                                BigDecimal planQuantity = salesOrderLine.getPlanQuantity();//计划执行数量
                                BigDecimal planRejectQuantity = salesOrderLine.getPlanRejectQuantity();//计划退货数量
                                //判断订单行是否执行完成：1计划数量等于零，2已执行数量减去已执行退货数量等于订单行数量
                                if (planQuantity.compareTo(BigDecimal.ZERO)!=0
                                        ||planRejectQuantity.compareTo(BigDecimal.ZERO)!=0
                                        ||quantity.compareTo(actualQuantity.subtract(actualRejectQuantity))!=0){
                                    salesOrderCompletionFlag = false;
                                    break;
                                }
                            }
                            if (salesOrderCompletionFlag){
                                salesOrder.setOrderStatus(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode());//完成
                                salesOrder.setExecuteTime(new Date());
                                JpaUtil.update(salesOrder);
                            }
                        }
                    }
                }
            }
        }
    }

    private boolean judgeWOCompletion(OrderContext context){//判断仓单是否已执行完毕
        Order order = context.getEntity();
        List<WarehouseOrderLine> lines = JpaUtil.linq(WarehouseOrderLine.class).equal("bizNo", order.getBizNo()).list();
        if (lines.size()>0){
            for (WarehouseOrderLine line:lines){
                BigDecimal completeQuantity = context.getLotService().getMaxQuantityByWo(line.getBizNo(), line.getLineNo());
                if (line.getQuantity().compareTo(completeQuantity)!=0){
                    return false;
                }
            }
        }else{
            return false;
        }
        return true;
    }
}
